#include <stdio.h>
#include <stdlib.h>

#include "od_sip_GJB_xml_cmd_define.h"
#include "od_sip_GB28181_2016_xml_cmd_define.h"
#include "od_sip_xml.h"
#include "od_def.h"
#include "od_errno.h"

/**@brief GJB*/
#define OD_SIP_XML_CONTROL       "control"
#define OD_SIP_XMl_RESPONSE      "response"
#define OD_SIP_XML_OPTSTATUS     "optstatus"

/**@brief G28181-2016*/
#define OD_SIP_XMl_NOTIFY        "Notify"
#define OD_SIP_XML_CTL           "Control"
#define OD_SIP_XML_QUERY         "Query"
#define OD_SIP_XMl_RESP          "Response"

/*query*/
#define GB28181_DeviceInfo       "DeviceInfo"
#define GB28181_Catalog          "Catalog"
#define GB28181_DeviceStatus     "DeviceStatus"
#define GB28181_ConfigDownload   "ConfigDownload"

/*control*/
#define GB28181_DeviceControl    "DeviceControl"
/** G28181-2016 end*/

#define IFRAMECONFREQ       "iframeconfreq"        //I帧间隔设置请求
#define PTZCTRL             "ptzctrl"              //云镜控制请求
#define POINTINDEXOPERATION "pointindexoperation"  //预置位操作请求
#define SETOSD              "setosd"               //单条OSD设置请求
#define SETOSDS             "setosds"              //多条OSD设置请求
#define CUSTOMOSD           "customosd"            //自定义OSD设置请求


static void sip_xml_build_respone(char *response_str, 
                                    int respone_bufsize,
                                    void *params, 
                                    struct pdesc *pdesc,
                                    char *root_str)
{
    xmlDocPtr doc = NULL;
    xmlNodePtr root;
    xmlChar *xmlbuff;
    int buffersize;

    doc = xmlNewDoc((xmlChar *)"1.0");
    if(!doc){
        RPTERR("xmlNewDoc() return NULL, error");
        return;
    }

    root = xmlNewDocNode(doc, NULL, (const xmlChar*)root_str, NULL);
    param2xml(params, root, pdesc); /* param -> xml */
    xmlDocSetRootElement(doc, root);
    xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
    snprintf(response_str, respone_bufsize, "%s", (char *)xmlbuff);
    xmlFree(xmlbuff);
    xmlFreeDoc(doc);	
}


static int sip_xml_dev_ptzctrl(od_sip_ctx *ctx, 
                                 xmlNodePtr root,
                                 char *response_str, int respone_bufsize,
                                 char *content_type, int content_bufsize)
{
    ptzctrl params_ptzctrl;
    msg_response response;

    memset(&params_ptzctrl, 0 ,sizeof(ptzctrl));
    xml2param(&params_ptzctrl, root, ptzctrl_params);

    /*callback to drive ptzctrl*/
    if (ctx->cb.ptzctl_cb.ptzctl)
        ctx->cb.ptzctl_cb.ptzctl(ctx->cb.ptzctl_cb.h_ptzctl, &params_ptzctrl);

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
    snprintf(response.seq, sizeof(response.seq), "%s", params_ptzctrl.seq);
    strcpy(response.response, "200");
    snprintf(response.ts, sizeof(response.ts), "%s", params_ptzctrl.ts);

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
    snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

    return OD_OK;
}


static int sip_xml_dev_iframedelta(od_sip_ctx *ctx, 
                                     xmlNodePtr root,
                                     char *response_str, int respone_bufsize,
                                     char *content_type, int content_bufsize)
{
    msg_iframeconfreq params_iframeconfreq;
    msg_response response;

    memset(&params_iframeconfreq, 0, sizeof(msg_iframeconfreq));
    xml2param(&params_iframeconfreq, root, iframeconfreq_params);

    /*callback to set gop len*/
    if (ctx->cb.gop_set_cb.gop_set)
        ctx->cb.gop_set_cb.gop_set(ctx->cb.gop_set_cb.h_gop, &params_iframeconfreq);

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
    snprintf(response.seq, sizeof(response.seq), "%s", params_iframeconfreq.seq);
    strcpy(response.response, "200");
    snprintf(response.ts, sizeof(response.ts), "%s", params_iframeconfreq.ts);

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
    snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

    return OD_OK;
}

static int sip_xml_dev_ptzposition(od_sip_ctx *ctx, 
                                     xmlNodePtr root,
                                     char *response_str, int respone_bufsize,
                                     char *content_type, int content_bufsize)
{
    ptzposition params_ptzpos;
    msg_response response;

    memset(&params_ptzpos, 0, sizeof(msg_iframeconfreq));
    xml2param(&params_ptzpos, root, ptzposition_params);

    if (ctx->cb.ptzctl_cb.ptzpos)
        ctx->cb.ptzctl_cb.ptzpos(ctx->cb.ptzctl_cb.h_ptzpos, &params_ptzpos);

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
    snprintf(response.seq, sizeof(response.seq), "%s", params_ptzpos.seq);
    strcpy(response.response, "200");
    snprintf(response.ts, sizeof(response.ts), "%s", params_ptzpos.ts);

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
    snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

    return OD_OK;
}
                                 
                                 
static int sip_xml_dev_setosd(od_sip_ctx *ctx, 
                                  xmlNodePtr root,
                                  char *response_str, int respone_bufsize,
                                  char *content_type, int content_bufsize)
{
     msg_osd params_osd;
     msg_header header;
     msg_response response;

     memset(&params_osd, 0, sizeof(msg_osd));
     memset(&header, 0, sizeof(msg_header));
     xml2param(&header, root, msghdr_params);
     xml2param(&params_osd, root, msgosd_params);

     if (ctx->cb.osd_cb.osd)
         ctx->cb.osd_cb.osd(ctx->cb.osd_cb.h_osd, &params_osd);

     /*create operation response*/
     memset(&response, 0, sizeof(response));
     strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
     snprintf(response.seq, sizeof(response.seq), "%s", header.seq);
     strcpy(response.response, "200");
     snprintf(response.ts, sizeof(response.ts), "%s", header.ts);

     sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
     snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

     return OD_OK;
}

static int sip_xml_dev_setosds(od_sip_ctx *ctx, 
                                 xmlNodePtr root,
                                 char *response_str, int respone_bufsize,
                                 char *content_type, int content_bufsize)
{
    msg_response response;
    msg_osd params_osd;
    msg_header header;
    msg_osd_multi multi_osd;
    xmlNode *sub_xnode = NULL;
    xmlNode *osditems = NULL;
    int array_size = sizeof(multi_osd.osd)/sizeof(multi_osd.osd[0]) - 1;

    /*get header*/
    memset(&header, 0, sizeof(header));
    xml2param(&header, root, msghdr_params);

    for(sub_xnode = root->xmlChildrenNode; sub_xnode; sub_xnode = sub_xnode->next)
    {
        if(!xmlStrcasecmp(sub_xnode->name, (const xmlChar*)"osdlist"))
            break;
    }

    if (!sub_xnode) {
        RPTERR("setosds sub_xnode NULL error!!");
        return OD_ERROR;
    }

    memset(&multi_osd, 0, sizeof(multi_osd));
    for(osditems = sub_xnode->xmlChildrenNode; osditems; osditems = osditems->next)
    {
        if(!xmlStrcasecmp(osditems->name, (const xmlChar*)"osditem")) {
            memset(&params_osd, 0, sizeof(params_osd));
            xml2param(&params_osd, osditems, msgosd_params);
            memcpy(&multi_osd.osd[multi_osd.array_size++], &params_osd, sizeof(params_osd));
            if (multi_osd.array_size > array_size) 
                break;
        }
    }

    if (ctx->cb.osd_cb.osds)
        ctx->cb.osd_cb.osds(ctx->cb.osd_cb.h_osds, &multi_osd);

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
    snprintf(response.seq, sizeof(response.seq), "%s", header.seq);
    strcpy(response.response, "200");
    snprintf(response.ts, sizeof(response.ts), "%s", header.ts);

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
    snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

    return OD_OK;
}


static int sip_xml_dev_setcustomosd(od_sip_ctx *ctx, 
                                      xmlNodePtr root,
                                      char *response_str, int respone_bufsize,
                                      char *content_type, int content_bufsize)
{
     msg_osd_customosd params_osd;
     msg_response response;

     memset(&params_osd, 0, sizeof(msg_osd));
     xml2param(&params_osd, root, msg_customosd_params);

     if (ctx->cb.osd_cb.customosd)
         ctx->cb.osd_cb.customosd(ctx->cb.osd_cb.h_customosd, &params_osd);

     /*create operation response*/
     memset(&response, 0, sizeof(response));
     strcpy(response.commandname, (char *)OD_SIP_XML_OPTSTATUS);
     snprintf(response.seq, sizeof(response.seq), "%s", params_osd.seq);
     strcpy(response.response, "200");
     snprintf(response.ts, sizeof(response.ts), "%s", params_osd.ts);

     sip_xml_build_respone(response_str, respone_bufsize, &response, 
        response_params, (char *)OD_SIP_XMl_RESPONSE);
     snprintf(content_type, content_bufsize, "%s", (char *)CONTENT_XML);

     return OD_OK;
}

/*GB28181-2016*/
int od_sip_build_heartbeat_xml(od_sip_ctx *ctx, 
                                 char *response_str, 
                                 int respone_bufsize,
                                 void *response)
{
    sip_xml_build_respone(response_str, 
                          respone_bufsize, 
                          response, 
                          msg_status_params, 
                          (char *)OD_SIP_XMl_NOTIFY);
    return OD_OK;
}

/**
* GJB A.3.1 Device control command response
*/
int od_sip_xml_dev_ctl_cmd_response(od_sip_ctx *ctx, 
                                       eXosip_event_t *event,
                                       char *response_str, int respone_bufsize,
                                       char *content_type, int content_bufsize)
{
    osip_body_t *body = NULL;
    xmlDocPtr pdoc = NULL;
    xmlNodePtr root;
    msg_request cmd_type_name;

    if (osip_message_get_body(event->request, 0, &body) < 0) {
        RPTERR("osip_message_get_body error!!");
        return OD_ERROR;
    }

    pdoc = xmlReadMemory(body->body, strlen(body->body), NULL, "utf8", 
             XML_PARSE_NODICT|XML_PARSE_COMPACT|XML_PARSE_NOBLANKS|XML_PARSE_NOERROR|XML_PARSE_NOWARNING);
    if(!pdoc) {
        RPTERR("xmlReadMemory, error!!!");
        return OD_ERROR;
    }

    root = xmlDocGetRootElement(pdoc);
    if (!root) {
        RPTERR("empty document\n");
        return OD_ERROR;
    }

    if (xmlStrcasecmp(root->name, (const xmlChar *) OD_SIP_XML_CONTROL)) {
        RPTERR("root node:[%s] != control", root->name);
        return OD_ERROR;
    }

    memset(&cmd_type_name, 0, sizeof(msg_request));
    xml2param(&cmd_type_name, root, cmd_name);

    RPTDBG("%s", cmd_type_name.commandname);
    
    if (!strcasecmp(cmd_type_name.commandname, PTZCTRL)) {
        return sip_xml_dev_ptzctrl(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.commandname, IFRAMECONFREQ)) {
        return sip_xml_dev_iframedelta(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.commandname, POINTINDEXOPERATION)) {
        return sip_xml_dev_ptzposition(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.commandname, SETOSD)) {
        return sip_xml_dev_setosd(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.commandname, SETOSDS)) {
        return sip_xml_dev_setosds(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.commandname, CUSTOMOSD)) {
        return sip_xml_dev_setcustomosd(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else {
        RPTERR("unsupport cmd:[%s]", cmd_type_name.commandname);
        return OD_ERROR;
    }
    
    return OD_OK;
}

/**
* @brief 以下xml解析和构建都基于GB28181协议
*/
static int sip_xml_deviceinfo(od_sip_ctx *ctx, 
                                xmlNodePtr root,
                                char *response_str, int respone_bufsize,
                                char *content_type, int content_bufsize)
{
    msg_DeviceInfo_rq request;
    msg_DeviceInfo_rp response;

    memset(&request, 0 ,sizeof(request));
    xml2param(&request, root, msg_deviceinfo_rq);

    if (strcasecmp(request.DeviceID, ctx->share->acc_info[0].username)) {
        RPTERR("Not query My Dev!! rq[%s],my[%s]", request.DeviceID, ctx->share->acc_info[0].username);
        return OD_ERROR;
    }

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.CmdType, GB28181_DeviceInfo);
    snprintf(response.SN, sizeof(response.SN), "%s", request.SN);
    snprintf(response.DeviceID, sizeof(response.DeviceID), "%s", ctx->share->acc_info[0].username);
    strcpy(response.Result, "OK");
    strcpy(response.Manufacturer, "SumaTech.co.Ltd");
    strcpy(response.Model, "AlphaGo5000");
    strcpy(response.Firmware, "2010.10263,V1025.32215");
    response.Channel = 2;

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
                                msg_deviceinfo_rp, (char *)OD_SIP_XMl_RESP);
    snprintf(content_type, content_bufsize, "%s", (char *)MANSCDP_XML);

    return OD_OK;
}


static int sip_xml_catalog(od_sip_ctx *ctx, 
                            xmlNodePtr root,
                            char *response_str, int respone_bufsize,
                            char *content_type, int content_bufsize)
{
    msg_Catalog_rq request;
    msg_Catalog_rp response;

    memset(&request, 0 ,sizeof(request));
    xml2param(&request, root, msg_catalog_rq);

    if (strcasecmp(request.DeviceID, ctx->share->acc_info[0].username)) {
        RPTERR("Not query My Dev!! rq[%s],my[%s]", request.DeviceID, ctx->share->acc_info[0].username);
        return OD_ERROR;
    }

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.CmdType, GB28181_Catalog);
    snprintf(response.SN, sizeof(response.SN), "%s", request.SN);
    snprintf(response.DeviceID, sizeof(response.DeviceID), "%s", ctx->share->acc_info[0].username);
    response.SumNum = 0;


    sip_xml_build_respone(response_str, respone_bufsize, &response, 
                                msg_catalog_rp, (char *)OD_SIP_XMl_RESP);
    snprintf(content_type, content_bufsize, "%s", (char *)MANSCDP_XML);

    return OD_OK;
}

static int sip_xml_devicestatus(od_sip_ctx *ctx, 
                                  xmlNodePtr root,
                                  char *response_str, int respone_bufsize,
                                  char *content_type, int content_bufsize)
{
    msg_DeviceStatus_rq request;
    msg_DeviceStatus_rp response;

    memset(&request, 0 ,sizeof(request));
    xml2param(&request, root, msg_devicestatus_rq);

    if (strcasecmp(request.DeviceID, ctx->share->acc_info[0].username)) {
        RPTERR("Not query My Dev!! rq[%s],my[%s]", request.DeviceID, ctx->share->acc_info[0].username);
        return OD_ERROR;
    }

    /*TODO: should get response from CallBack!!*/

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.CmdType, GB28181_DeviceStatus);
    snprintf(response.SN, sizeof(response.SN), "%s", request.SN);
    snprintf(response.DeviceID, sizeof(response.DeviceID), "%s", ctx->share->acc_info[0].username);
    strcpy(response.Result, "OK");
    strcpy(response.Online, "ONLINE");
    strcpy(response.Status, "OK");
    strcpy(response.Encode, "ON");
    strcpy(response.Record, "OFF");
    strcpy(response.DeviceTime, "2019-11-25 T11:06:25");

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
                                msg_devicestatus_rp, (char *)OD_SIP_XMl_RESP);
    snprintf(content_type, content_bufsize, "%s", (char *)MANSCDP_XML);

    return OD_OK;
}


static int sip_xml_configdownload(od_sip_ctx *ctx, 
                                    xmlNodePtr root,
                                    char *response_str, int respone_bufsize,
                                    char *content_type, int content_bufsize)
{
    msg_ConfigDownload_rq request;
    msg_ConfigDownload_rp response;

    memset(&request, 0 ,sizeof(request));
    xml2param(&request, root, msg_configdownload_rq);

    if (strcasecmp(request.DeviceID, ctx->share->acc_info[0].username)) {
        RPTERR("Not query My Dev!! rq[%s],my[%s]", request.DeviceID, ctx->share->acc_info[0].username);
        return OD_ERROR;
    }

    /*TODO: should get response from CallBack!!*/

    /*create operation response*/
    memset(&response, 0, sizeof(response));
    strcpy(response.CmdType, GB28181_ConfigDownload);
    snprintf(response.SN, sizeof(response.SN), "%s", request.SN);
    snprintf(response.DeviceID, sizeof(response.DeviceID), "%s", ctx->share->acc_info[0].username);
    strcpy(response.Result, "OK");

    /*those value only for test! you should call callback-func to get those param
    * in your specific device.
    */
    if (!strcasecmp(request.ConfigType, "BasicParam")) {
        strcpy(response.BasicParam.Name, "Odin-sip");
        strcpy(response.BasicParam.Expiration, "60");
        response.BasicParam.HeartBeatCount = 3;
        response.BasicParam.HeartBeatInterval = 60;
        response.BasicParam.PositionCapability = 0;
    } else if (!strcasecmp(request.ConfigType, "videoParamOpt")) {
        strcpy(response.VideoParamOpt.DownloadSpeed, "10M/s");
        strcpy(response.VideoParamOpt.Resolution, "1920x1080@30fps");
    }

    sip_xml_build_respone(response_str, respone_bufsize, &response, 
                              msg_configdownload_rp, (char *)OD_SIP_XMl_RESP);
    snprintf(content_type, content_bufsize, "%s", (char *)MANSCDP_XML);

    return OD_OK;
}

/*GB28181 Query*/
int od_sip_xml_query_process(od_sip_ctx *ctx, 
                               eXosip_event_t *event,
                               char *response_str, int respone_bufsize,
                               char *content_type, int content_bufsize)
{
    osip_body_t *body = NULL;
    xmlDocPtr pdoc = NULL;
    xmlNodePtr root;
    msg_CmdType cmd_type_name;
    char *xml_body_start = NULL;

    if (osip_message_get_body(event->request, 0, &body) < 0) {
        RPTERR("osip_message_get_body error!!");
        return OD_ERROR;
    }

    xml_body_start = strstr(body->body, "<?xml");
    if (!xml_body_start) {
        RPTERR("get xml header error!!");
        return OD_ERROR;
    }
    
    pdoc = xmlReadMemory(xml_body_start, strlen(xml_body_start), NULL, "utf8", 
             XML_PARSE_NODICT|XML_PARSE_COMPACT|XML_PARSE_NOBLANKS|XML_PARSE_NOERROR|XML_PARSE_NOWARNING);
    if(!pdoc) {
        RPTERR("xmlReadMemory, error!!!");
        return OD_ERROR;
    }

    root = xmlDocGetRootElement(pdoc);
    if (!root) {
        RPTERR("empty document error!");
        return OD_ERROR;
    }

    if (xmlStrcasecmp(root->name, (const xmlChar *) OD_SIP_XML_QUERY)) {
        RPTERR("root node:[%s] != %s, error!", root->name, OD_SIP_XML_QUERY);
        return OD_ERROR;
    }

    memset(&cmd_type_name, 0, sizeof(msg_request));
    xml2param(&cmd_type_name, root, msg_cmdtype);

    RPTDBG("%s", cmd_type_name.CmdType);

    if (!strcasecmp(cmd_type_name.CmdType, GB28181_DeviceInfo)) {
        return sip_xml_deviceinfo(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.CmdType, GB28181_Catalog)) {
        return sip_xml_catalog(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.CmdType, GB28181_DeviceStatus)) {
        return sip_xml_devicestatus(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else if (!strcasecmp(cmd_type_name.CmdType, GB28181_ConfigDownload)) {
        return sip_xml_configdownload(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
    }else {
        RPTERR("unsupport cmd:[%s]", cmd_type_name.CmdType);
        return OD_ERROR;
    }

    return OD_OK;
}


static int sip_xml_devicecontrol(od_sip_ctx *ctx, 
                                   xmlNodePtr root,
                                   char *response_str, int respone_bufsize,
                                   char *content_type, int content_bufsize)
{
   msg_DeviceStatus_rq request;
   msg_DeviceControl_rp response;

   memset(&request, 0 ,sizeof(request));
   xml2param(&request, root, msg_devicestatus_rq);

   if (strcasecmp(request.DeviceID, ctx->share->acc_info[0].username)) {
       RPTERR("Not query My Dev!! rq[%s],my[%s]", request.DeviceID, ctx->share->acc_info[0].username);
       //return OD_ERROR;
   }

   /*TODO: should get response from CallBack!!*/

   /*create operation response*/
   memset(&response, 0, sizeof(response));
   strcpy(response.CmdType, GB28181_DeviceControl);
   snprintf(response.SN, sizeof(response.SN), "%s", request.SN);
   snprintf(response.DeviceID, sizeof(response.DeviceID), "%s", ctx->share->acc_info[0].username);
   strcpy(response.Result, "OK"); 

   sip_xml_build_respone(response_str, respone_bufsize, &response, 
                               msg_devicecontrol_rp, (char *)OD_SIP_XMl_RESP);
   snprintf(content_type, content_bufsize, "%s", (char *)MANSCDP_XML);

   return OD_OK;
}

static void sip_xml_IFameCmd(od_sip_ctx *ctx, 
                                   xmlNodePtr root,
                                   char *response_str, int respone_bufsize,
                                   char *content_type, int content_bufsize)
{
    if (ctx->cb.keyframe_cb.keyframe)
        ctx->cb.keyframe_cb.keyframe(ctx->cb.keyframe_cb.h_keyframe);
}

static void sip_xml_PTZCmd(od_sip_ctx *ctx, 
                                   xmlNodePtr root,
                                   char *response_str, int respone_bufsize,
                                   char *content_type, int content_bufsize)
{
    msg_PTZCmd request;
    uint64_t u64PTZCmd;
    ptzctrl cmd;
    uint8_t ptz_cmd;       //字节4 PTZ指令
    //uint8_t ptz_tiltspeed; //垂直速度
    uint8_t ptz_panspeed;  //水平速度
    uint8_t ptz_zoomspeed; //变倍速度
    //uint8_t ptz_focus;         //聚焦
    //uint8_t ptz_aperture;      //光圈
    //uint8_t ptz_focusspeed;    //聚焦速度
    //uint8_t ptz_aperturespeed; //光圈速度
    uint8_t cmd_type;

    memset(&cmd, 0, sizeof(cmd));
    memset(&request, 0 ,sizeof(request));
    xml2param(&request, root, msg_PTZCmd_rq);

    RPTDBG("PTZCmd:%s", request.PTZCmd);
    u64PTZCmd = strtoull((const char *)&request.PTZCmd, NULL, 16);

    ptz_cmd = (u64PTZCmd >> 32) & 0xFF;
    ptz_panspeed = (u64PTZCmd >> 24) & 0xFF;
    //ptz_tiltspeed = (u64PTZCmd >> 16) & 0xFF;
    ptz_zoomspeed = (u64PTZCmd >> (8+4)) & 0x0F;  //高4位时变焦速度

    cmd_type = (ptz_cmd >> 6) & 0x03;
    if (cmd_type == 0x0) { //PTZ指令
        uint8_t zoom = (ptz_cmd>>4) & 0x03;
        strcpy(cmd.operation, GJB_START);
        cmd.zoom.speed = ptz_zoomspeed;
        if (zoom == 0x01) { //放大
            strcpy(cmd.zoom.zoomctrl, GJB_ZOOMIN);
        } else if (zoom == 0x02) { //缩小
            strcpy(cmd.zoom.zoomctrl, GJB_ZOOMOUT);
        } else {
            strcpy(cmd.operation, GJB_STOP);
        }

        if (ctx->cb.ptzctl_cb.ptzctl)
            ctx->cb.ptzctl_cb.ptzctl(ctx->cb.ptzctl_cb.h_ptzctl, &cmd);

        uint8_t tilt = ptz_cmd & 0x0F;; //方向
        strcpy(cmd.operation, GJB_START);
        cmd.tilt.speed = ptz_panspeed;
        if (tilt == 0x01) { //右
            strcpy(cmd.tilt.direction, GJB_DIRECTION_RIGHT);
        } else if (tilt == 0x02) {  //左
            strcpy(cmd.tilt.direction, GJB_DIRECTION_LEFT);
        } else if (tilt == 0x4) { //下
            strcpy(cmd.tilt.direction, GJB_DIRECTION_DOWN);
        } else if (tilt == 0x8) { //上
            strcpy(cmd.tilt.direction, GJB_DIRECTION_UP);
        } else if (tilt == 0xA) { //上左
            strcpy(cmd.tilt.direction, GJB_DIRECTION_UPLEFT);
        } else if (tilt == 0x9) { //上右
            strcpy(cmd.tilt.direction, GJB_DIRECTION_UPRIGHT);
        } else if (tilt == 0x6) { //下左
            strcpy(cmd.tilt.direction, GJB_DIRECTION_DOWNLEFT);
        } else if (tilt == 0x5) { //下右
            strcpy(cmd.tilt.direction, GJB_DIRECTION_DOWNRIGHT);
        } else {
            strcpy(cmd.operation, GJB_STOP);
        }

        if (ctx->cb.ptzctl_cb.ptzctl)
            ctx->cb.ptzctl_cb.ptzctl(ctx->cb.ptzctl_cb.h_ptzctl, &cmd);
    }else if (cmd_type == 0x01) { //FI指令(光圈)
        //TODO
    } else if (cmd_type == 0x02) {
        ptzposition position; //预置位指令
        
        if (ptz_cmd == 0x81) { //设置预置位
            strcpy(position.operation, GJB_POINTINDEXOPERATION_ADD);
            position.pointindexvalue = (u64PTZCmd>>16) & 0xFF;
        
            if (ctx->cb.ptzctl_cb.ptzpos)
                ctx->cb.ptzctl_cb.ptzpos(ctx->cb.ptzctl_cb.h_ptzpos, &position);
        } else if (ptz_cmd == 0x82) { //调用预置位
            strcpy(position.operation, GJB_POINTINDEXOPERATION_SET);
            position.pointindexvalue = (u64PTZCmd>>16) & 0xFF;
            
            if (ctx->cb.ptzctl_cb.ptzpos)
                ctx->cb.ptzctl_cb.ptzpos(ctx->cb.ptzctl_cb.h_ptzpos, &position);
        } else if (ptz_cmd == 0x83) { //删除预置位
            strcpy(position.operation, GJB_POINTINDEXOPERATION_REMOVE);
            position.pointindexvalue = (u64PTZCmd>>16) & 0xFF;
            
            if (ctx->cb.ptzctl_cb.ptzpos)
                ctx->cb.ptzctl_cb.ptzpos(ctx->cb.ptzctl_cb.h_ptzpos, &position);
        } else if (ptz_cmd == 0x84) {
            //TODO 加入巡航点
        } else if (ptz_cmd == 0x85) {
            //TODO 删除一个巡航点
        } else if (ptz_cmd == 0x86) {
            //TODO 设置巡航速度
        } else if (ptz_cmd == 0x87) {
            //TODO 设置巡航停留时间
        } else if (ptz_cmd == 0x88) {
            //TODO 开始巡航
        } else if (ptz_cmd == 0x89) {
            //TODO 扫描指令
        } else if (ptz_cmd == 0x8A) {
            //TODO 扫描指令
        } else if (ptz_cmd == 0x8C) {
            //TODO 辅助开关
        } else if (ptz_cmd == 0x8D) {
            //TODO 辅助开关
        }
    }    
}
                                   
/*GB28181 Control*/
int od_sip_xml_control_process(od_sip_ctx *ctx, 
                                 eXosip_event_t *event,
                                 char *response_str, int respone_bufsize,
                                 char *content_type, int content_bufsize)
{
   osip_body_t *body = NULL;
   xmlDocPtr pdoc = NULL;
   xmlNodePtr root;
   msg_CmdType cmd_type_name;
   char *xml_body_start = NULL;

   if (osip_message_get_body(event->request, 0, &body) < 0) {
       RPTERR("osip_message_get_body error!!");
       return OD_ERROR;
   }

   xml_body_start = strstr(body->body, "<?xml");
   if (!xml_body_start) {
       RPTERR("get xml header error!!");
       return OD_ERROR;
   }
   
   pdoc = xmlReadMemory(xml_body_start, strlen(xml_body_start), NULL, "utf8", 
            XML_PARSE_NODICT|XML_PARSE_COMPACT|XML_PARSE_NOBLANKS|XML_PARSE_NOERROR|XML_PARSE_NOWARNING);
   if(!pdoc) {
       RPTERR("xmlReadMemory, error!!!");
       return OD_ERROR;
   }

   root = xmlDocGetRootElement(pdoc);
   if (!root) {
       RPTERR("empty document error!");
       return OD_ERROR;
   }

   if (xmlStrcasecmp(root->name, (const xmlChar *) OD_SIP_XML_CTL)) {
       RPTERR("root node:[%s] != %s, error!", root->name, OD_SIP_XML_CTL);
       return OD_ERROR;
   }

   memset(&cmd_type_name, 0, sizeof(msg_request));
   xml2param(&cmd_type_name, root, msg_cmdtype);

   RPTDBG("%s", cmd_type_name.CmdType);

   if (!strcasecmp(cmd_type_name.CmdType, GB28181_DeviceControl)) {
       if (strstr(body->body, "IFameCmd")) {
           sip_xml_IFameCmd(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
       } else if (strstr(body->body, "PTZCmd")) {
           sip_xml_PTZCmd(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
       }
       
       return sip_xml_devicecontrol(ctx, root, response_str, respone_bufsize, content_type, content_bufsize);
   }else {
       RPTERR("unsupport cmd:[%s]", cmd_type_name.CmdType);
       return OD_ERROR;
   }

   return OD_OK;
}

/*_*/

